package network;

import java.io.*;
import java.net.*;
import java.util.Iterator;
import java.util.LinkedList;

import util.Lib;

public class Communicator {
	static final boolean DEBUG = true;
	static final int WAIT_TIME = 3000;
	static final int BROADCAST_PORT = 9000;
	static final int MAX_TRY_MAIN_CLIENT = 2;
	boolean isSpeaker;
	InetAddress serverIP, oppositeIP;
	int serverPort;
	LinkedList<Socket> notifyList;
	InputStream socketIS;
	OutputStream socketOS;
	ServerSocket ss;
	Communicator father;
	BroadcastListener bdListener;

	
	public Communicator(InetAddress targetIP, int targetPort) {
		serverIP = targetIP;
		serverPort = targetPort;
		isSpeaker = false;
	}

	public Socket findServer() {
		Lib.assertTrue(isSpeaker == false);
		DatagramSocket searcher = null;
		DatagramPacket packet = null;
		
		int myPort = 7000;
		String myIP = null;
		String serverIPStr = serverIP.toString().substring(1); 
		try {
			searcher = new DatagramSocket();
			myIP = InetAddress.getLocalHost().getHostAddress();
			ss = new ServerSocket(myPort);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		while (ss == null)
			try {
				Thread.sleep(1000);
				ss = new ServerSocket(myPort);
			} catch (Exception e) {
				e.printStackTrace();
			}
		
		for (int i=0; i<MAX_TRY_MAIN_CLIENT; ++i) {
			try {
				String sendInfo = serverIPStr + " " + serverPort
						+ " " + myIP + " " + myPort + " #";
				println(sendInfo);
				byte[] byteval = sendInfo.getBytes();
				packet = new DatagramPacket(byteval, byteval.length, 
						InetAddress.getByName("255.255.255.255"), BROADCAST_PORT);
				searcher.send(packet);
				ss.setSoTimeout(WAIT_TIME);
				Socket s = ss.accept();
				ss.close();
				return s;
			} catch (Exception e) {
				if (e instanceof SocketTimeoutException) continue;
				else
					try {
						ss.close();
						++myPort;
						ss = new ServerSocket(myPort);
					} catch (Exception e1) {
						e1.printStackTrace();
					}
				e.printStackTrace();
			}
		}
		
		try {
			ss.close();
			searcher.close();
		} catch (IOException e) {
		}
		return null;
	}
	
	public void startListen() {
		isSpeaker = true;
		bdListener = new BroadcastListener(serverIP, serverPort, this);
		bdListener.start();
		notifyList =new LinkedList<Socket>();
	}
	
	public void connectTo(String targetIP, int targetPort) {
		try {
			InetSocketAddress endpoint = new InetSocketAddress(targetIP, targetPort);
			Socket socket = new Socket();
			socket.connect(endpoint);
			notifyList.addLast(socket);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void closeBroadcastListener() {
		Lib.assertTrue(isSpeaker == true);
		bdListener.close();
		Iterator<Socket> iter = notifyList.iterator();
		while (iter.hasNext())
			try {
				iter.next().close();
			} catch (IOException e) {
				e.printStackTrace();
			}
	}
	
	private void println(Object o) {
		if (DEBUG == false) return;
		System.out.println(o);
	}

	public void notifyOthers(String res) {
		if (notifyList == null) return;
		Iterator<Socket> iter = notifyList.iterator();
		Socket so = null;
		while (iter.hasNext()) {
			try {
				so = iter.next();
				OutputStream os = so.getOutputStream();
				os.write(res.getBytes());
			} catch (java.net.SocketException e) {
				try {
					so.close();
				} catch (IOException e1) {}
				iter.remove();
				continue;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
